﻿

namespace Pamela.Logic.Engines.Base
{
    using System;
    using System.Collections.Generic;
    using System.Reflection;

    using Pamela.Logic.Exceptions;
    using Pamela.Logic.Helpers;
    using Pamela.Logic.Imps;
    using Pamela.Logic.Layers.Base;
    using Pamela.Logic.Layers.Mvc;
    using Pamela.Logic.ProjectFiles;
    using Pamela.Logic.ProjectFiles.Base;
    using Pamela.Logic.Engines.Base;

    public abstract class BaseScaffoldingEngine
    {
        /// <summary>
        /// The shared layer assembly object
        /// </summary>
        protected Assembly assembly;

        /// <summary>
        /// Builder  object containing all the info to necessary perform the scaffolding operation
        /// </summary>
        protected BuilderInfo builderInfo;

        /// <summary>
        /// The collection of layer objects defined for this scaffolding engine
        /// </summary>
        protected List<BaseLayer> layersCollection;

        protected BaseScaffoldingEngine(Assembly assembly, BuilderInfo builderInfo)
        {
            if (assembly == null)
            {
                throw new ScaffoldingException("The shared assembly has not been initialized");
            }

            if (builderInfo == null)
            {
                throw new ScaffoldingException("The builder info has not ben initialized");
            }

            if (string.IsNullOrEmpty(builderInfo.SelectedModel))
            {
                throw new ScaffoldingException("The builder info attribute SelectedModel has not ben initialized");
            }

            this.assembly = assembly;
            this.builderInfo = builderInfo;
            this.layersCollection = new List<BaseLayer>();

            this.RegisterLayers();
            this.ValidateProjectFiles();
        }

        /// <summary>
        /// Invoke the build action for each element in the builder collection
        /// </summary>
        public void Build()
        {
            bool error = false;
            string errorMessage = string.Empty;

            try
            {
                this.UpdateProjectFiles();

                foreach (var layer in this.layersCollection)
                {
                    layer.Build();
                }
            }
            catch (Exception e)
            {
                this.RollbackProjectFiles();

                error = true;
                errorMessage = e.Message;
            }
            finally
            {
                this.DisposeProjectFiles();

                if (error)
                {
                    throw new ScaffoldingException(errorMessage);
                }
            }
        }

        /// <summary>
        /// Register all internal layer objects
        /// </summary>
        protected abstract void RegisterLayers();

        /// <summary>
        /// Validate if any of the Visual Studio project file already contains entries for the files that will be included
        /// </summary>
        protected void ValidateProjectFiles()
        {
            foreach (var layer in this.layersCollection)
            {
                if (layer.ProjectFile.Validate() == false)
                {
                    throw new ScaffoldingException("One or more project files already contain information about the selected model");
                }
            }
        }

        /// <summary>
        /// Update all project files to reflect the change back to  Visual Studio
        /// </summary>
        protected void UpdateProjectFiles()
        {
            foreach (var layer in this.layersCollection)
            {
                layer.ProjectFile.Update();
            }
        }

        /// <summary>
        /// Dispose all project files
        /// </summary>
        protected void DisposeProjectFiles()
        {
            foreach (var layer in this.layersCollection)
            {
                layer.ProjectFile.Dispose();
            }
        }

        /// <summary>
        /// Rollback changes made to all project files
        /// </summary>
        protected void RollbackProjectFiles()
        {
            foreach (var layer in this.layersCollection)
            {
                layer.ProjectFile.Rollback();
            }
        }
    }
}
